﻿#include "../../x/draw/draw.h"

namespace swui
{	
	namespace
	{
		template<typename DrawFunction>
		void alphaDraw(DrawFunction func, RECT& rc, ctlColor color, HDC hdc1, HDC hdc2, ctlRoot* root, bool isBitBlt = true)
		{
			BLENDFUNCTION bf = {0};
			bf.BlendOp = AC_SRC_OVER;
			bf.SourceConstantAlpha = color >> 24;
			//透明度绘制
			int width = rc.right - rc.left, height = rc.bottom - rc.top;
			int leftoff = root->getLeft();
			int topoff = root->getTop();
			if(isBitBlt) BitBlt(hdc1, rc.left - leftoff, rc.top - topoff,
				width, height, hdc2, rc.left, rc.top, SRCCOPY);
			func();
			AlphaBlend(hdc2, rc.left, rc.top, width, height, 
				hdc1, rc.left - leftoff, rc.top - topoff, width, height, bf);	
		}
	}
	void ctlEdit::_draw_selected_text(_ctlInterior::drawInfo* info, int format)
	{
		ctlStyleEdit* style = static_cast<ctlStyleEdit*>(_styleBase);
		ctlColor textcolor = style->textSelected;
		HDC hdc = info->hDC, bkhdc = info->hDC;
		//获取数据
		_edit_pos caretBegin(_edit_caret_pos_begin.section, 
			_edit_caret_pos_begin.line);
		_edit_pos caretEnd(_edit_caret_pos_end.section, 
			_edit_caret_pos_end.line);
		unsigned offsetBegin = _edit_caret_pos_begin.offset, 
			offsetEnd = _edit_caret_pos_end.offset;
		//重新排列前后顺序
		if(caretBegin.first > caretEnd.first ||
			caretBegin.first == caretEnd.first && 
			caretBegin.second > caretEnd.second)
		{
			std::swap(caretBegin, caretEnd);
			std::swap(offsetBegin, offsetEnd);
		}
		if(caretBegin.first == caretEnd.first && 
			caretBegin.second == caretEnd.second &&
			offsetBegin > offsetEnd) std::swap(offsetBegin, offsetEnd);
		//调整区域在可绘制部分内
		if(caretBegin.first < _edit_top_pos.first)
		{
			caretBegin.first = _edit_top_pos.first;
			caretBegin.second = 0;
			offsetBegin = 0;
		}
		if(caretBegin.first == _edit_top_pos.first 
			&& caretBegin.second < _edit_top_pos.second)
		{
			caretBegin.second = _edit_top_pos.second;
			offsetBegin = 0;
		}
		if(caretBegin.first > caretEnd.first ||
			caretBegin.first == caretEnd.first &&
			caretBegin.second > caretEnd.second)
			return;
		//设置 DC 信息
		bool selectBarAlpha = style->colorFormat == ctlColorFormat::rgba && 
				(style->selectBar >> 24) != 0xff;
		bool textAlpha = style->colorFormat == ctlColorFormat::rgba &&	
				(textcolor >> 24) != 0xff;
		auto& alphaDC = _eventTrans.getMajorInfo()->tempAlpha;
		if(textAlpha && selectBarAlpha) hdc = alphaDC.getDC();
		if(selectBarAlpha) bkhdc = alphaDC.getDC();
		SetTextColor(hdc, MAKE_SYSTEM_RGB(textcolor) & 0xffffff);
		if(selectBarAlpha == false)
		{
			SetBkMode(hdc, OPAQUE);
			SetBkColor(hdc, MAKE_SYSTEM_RGB(style->selectBar) & 0xffffff);
			if(textAlpha)
			{
				ctlColor newColor = style->textSelected;
				auto* src = reinterpret_cast<unsigned char*>(&newColor);
				auto* dest = reinterpret_cast<unsigned char*>(&style->selectBar);
				unsigned alpha = newColor >> 24;
				for(int i = 0; i < 4; ++i)
					src[i] = (src[i] * alpha + dest[i] * (255 - alpha)) / 255;
				SetTextColor(hdc, MAKE_SYSTEM_RGB(newColor) & 0xffffff);
			}
		}
		HBRUSH hBrush = CreateSolidBrush(MAKE_SYSTEM_RGB(style->selectBar) & 0xffffff);
		//绘制第一行
		unsigned rendd = offsetEnd, rbeginn = offsetBegin;
		if(caretBegin != caretEnd)
		{
			_textInfo* section = _getSection(caretBegin.first);
			auto pr = section->wrap[caretBegin.second];
			rendd = pr.second - pr.first;
		} else {
			rendd = ctlMax(rendd, offsetBegin);
			rbeginn = ctlMin(rbeginn, offsetEnd);
		}
		_exactPos eps;
		eps.section = caretBegin.first;
		eps.line = caretBegin.second;
		eps.offset = rbeginn;
		ctlPoint pt = _edit_map_pos_to_point(eps);
		_textInfo* sec = _getSection(eps.section);
		std::wstring::const_pointer txt = sec->str.wptr()
			+ sec->wrap[eps.line].first + rbeginn;
		RECT rc;
		SetRect(&rc, pt.x + getLeft(), pt.y + getTop(), 0, 0);
		rc.bottom = rc.top + getFont()->getFontHeight();
		if(rendd - rbeginn)
		{
			DrawTextW(hdc, txt, rendd - rbeginn, &rc, format | DT_CALCRECT);
			rc.bottom = ctlMin(rc.bottom, getBottom() - getHeightPadding());
			RECT rc2 = rc;
			OffsetRect(&rc2, -getLeft(), -getTop());
			if(selectBarAlpha) alphaDraw( [&] { FillRect(bkhdc, &rc2, hBrush);	},
				rc, style->selectBar, bkhdc, info->hDC, this, false);
			if(textAlpha && selectBarAlpha)
				alphaDraw( [&]{ DrawTextW(hdc, txt, rendd - rbeginn, &rc2, format);}, 
				rc, textcolor, hdc, info->hDC, this);
			else DrawTextW(hdc, txt, rendd - rbeginn, &rc, format);
		}
		//绘制其余部分
		if(caretBegin != caretEnd)
		{
			unsigned section = caretBegin.first, 
				line = caretBegin.second;
			_textInfo* si = _getSection(section);
			_edit_pos eps = si->wrap[line];
		
			unsigned lineHeight = getFont()->getFontHeight();
			rc.left = getLeftPadding() + getLeft();
			rc.right = getRight() - getRightPadding();
			while(rc.top < getBottom() - (int)getHeightPadding())
			{
				rc.top = rc.bottom + getLineLength();
				rc.bottom = ctlMin(rc.top + lineHeight, getBottom() - getHeightPadding());
				if(_getLineNumber(section) == ++line)
				{
					si = _getSection(++section);
					if(si == nullptr) break;
					if(si->wrap.empty()) break;
					line = 0;
				}
				unsigned textLen = offsetEnd;
				eps = si->wrap[line];
				bool breakFlag = false;
				if(section < caretEnd.first ||
					section == caretEnd.first && 
					line < caretEnd.second)
				{
					textLen = eps.second - eps.first;
				} else {
					breakFlag = true;
				}
				RECT rc2 = rc;
				if(textAlpha || selectBarAlpha) DrawTextW(hdc, &si->str.stdwstr()[eps.first], 
						textLen, &rc2, format | DT_NOPREFIX | DT_CALCRECT);
				DrawTextW(hdc, &si->str.stdwstr()[eps.first], 
					textLen, &rc2, format | DT_NOPREFIX | DT_CALCRECT);
				if(selectBarAlpha)
				{
					alphaDraw( [&] {
						RECT rc3 = rc2;
						OffsetRect(&rc3, -getLeft(), -getTop());
						FillRect(bkhdc, &rc3, hBrush);
					}, rc2, style->selectBar, bkhdc, info->hDC, this, false);
				}
				if(textAlpha && selectBarAlpha)
					alphaDraw( [&]{ 
						RECT rc3 = rc2;
						OffsetRect(&rc3, -getLeft(), -getTop());
						DrawTextW(hdc, &si->str.stdwstr()[eps.first], 
							textLen, &rc3, format | DT_NOPREFIX);
					}, rc2, textcolor, hdc, info->hDC, this);
				else DrawTextW(hdc, &si->str.stdwstr()[eps.first], 
					textLen, &rc, format | DT_NOPREFIX);
				if(breakFlag) break;
			}
		}
		SetBkMode(hdc, TRANSPARENT);
		DeleteObject(hBrush);
	}
	void ctlEdit::_redraw_(_ctlInterior::drawInfo* info)
	{
		_background_redraw(info);

		ctlStyleEdit* style = static_cast<ctlStyleEdit*>(_styleBase);
		ctlColor textcolor = style->textLeave;
		HDC hdc = info->hDC;
		auto& alphaDC = _eventTrans.getMajorInfo()->tempAlpha;
		alphaDC.require(getWidth(), getHeight());
		bool textColorAlpha = style->colorFormat == ctlColorFormat::rgba
			&& (textcolor >> 24) != 0xff;
		if(textColorAlpha) 
		{
			hdc = alphaDC.getDC();
			BitBlt(hdc, 0, 0, getWidth(), getHeight(), info->hDC, getLeft(), getTop(), SRCCOPY);
		}
		HFONT fontinstance = reinterpret_cast<HFONT>(getFont()->getNativeHandle());

		unsigned lineHeight = getFont()->getFontHeight();
		RECT rect;
		rect.left = getLeftPadding();
		rect.right = getWidth() - rect.left;
		rect.top = getHeightPadding();
		rect.bottom = _edit_top_offset + getHeightPadding();
		if(!textColorAlpha)
			OffsetRect(&rect, getLeft(), getTop());
		unsigned section = _edit_top_pos.first;
		unsigned line = _edit_top_pos.second;
		_textInfo* si = _getSection(section);
		if(si == nullptr) return;
		if(si->wrap.empty()) return;
		_edit_pos eps = si->wrap[line];

		if(GetTextColor(hdc) != (MAKE_SYSTEM_RGB(textcolor) & 0xffffff))
			SetTextColor(hdc, MAKE_SYSTEM_RGB(textcolor) & 0xffffff);
		HGDIOBJ oldFont, tempOldFont;
		oldFont = SelectObject(info->hDC, fontinstance);
		tempOldFont = SelectObject(alphaDC.getDC(), fontinstance);

		FORMAT_TEXT_ALIGN;
		DrawTextW(hdc, &si->str.stdwstr()[eps.first], 
			eps.second - eps.first, &rect, format | 
			DT_BOTTOM | DT_SINGLELINE | DT_NOPREFIX);
		int bottomMax = getBottom() - getHeightPadding();
		if(textColorAlpha) bottomMax -= getTop();
		while(rect.top < bottomMax)
		{
			rect.top = rect.bottom + getLineLength();
			rect.bottom = ctlMin(rect.top + lineHeight, bottomMax);
			if(_getLineNumber(section) == ++line)
			{
				si = _getSection(++section);
				if(si == nullptr) break;
				if(si->wrap.empty()) break;
				line = 0;
			}
			eps = si->wrap[line];
			DrawTextW(hdc, &si->str.stdwstr()[eps.first], 
				eps.second - eps.first, &rect, format | DT_NOPREFIX);
		}
		if(textColorAlpha) 
		{
			RECT rc = { getLeft(), getTop(), getRight(), rect.bottom + getTop() };
			alphaDraw( []{ }, rc, textcolor, hdc, info->hDC, this, false);
		}
		_draw_selected_text(info, format | DT_NOPREFIX);
		SelectObject(info->hDC, oldFont);
		SelectObject(alphaDC.getDC(), tempOldFont);
	}

	void ctlSingleEdit::_redraw_(_ctlInterior::drawInfo* info)
	{
		_background_redraw(info);
		
		if(_edit_str.empty()) return;
		ctlStyleEdit* style = static_cast<ctlStyleEdit*>(_styleBase);
		ctlColor textcolor = style->textLeave;
		HDC hdc = info->hDC;
		HFONT fontInstance = reinterpret_cast<HFONT>(getFont()->getNativeHandle());
		HGDIOBJ oldFont = SelectObject(info->hDC, fontInstance);
		SetTextColor(hdc, MAKE_SYSTEM_RGB(textcolor) & 0xffffff);
		
		std::wstring::const_pointer text = _textpack.wptr() + _edit_left_pos;
		RECT rc, rc2;
		rc.left = getLeft() + getLeftPadding();
		rc.top = getTop() + getHeightPadding();
		rc.right = rc.left + _edit_left_offset;
		rc.bottom = rc.top + getFont()->getFontHeight();
		rc2 = rc;
		unsigned txtlen = getTextLength(),
			rightmost = _edit_left_pos;

		DrawTextW(hdc, text, 1, &rc, DT_RIGHT | DT_NOPREFIX | DT_SINGLELINE);
		rc.left = rc.right;
		auto iter = _edit_str.begin() + rightmost;
		int lm = rc.left - *iter;
		while(iter != _edit_str.end() && lm < getWidth())
		{
			lm += *iter++;
			++rightmost;
		}
		rightmost -= _edit_left_pos;
		rc.right = getRight() - getRightPadding();
		DrawTextW(hdc, text + 1, ctlMin(rightmost, txtlen), &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE);

		unsigned begin = ctlMin(_edit_caret_pos_begin, _edit_caret_pos_end), 
			end = ctlMax(_edit_caret_pos_begin, _edit_caret_pos_end);
		if(begin != end && end > _edit_left_pos)
		{
			begin = ctlMax(begin, _edit_left_pos);
			text = _textpack.wptr() + begin;
			rc2.left = ctlMax(_edit_map_pos_to_point(begin).x, getLeft() + getLeftPadding());
			rc2.right = ctlMin(rc2.left + _edit_str[begin], getRight() - getRightPadding());

			SetTextColor(hdc, MAKE_SYSTEM_RGB(style->textSelected & 0xffffff));
			SetBkMode(hdc, OPAQUE);
			SetBkColor(hdc, MAKE_SYSTEM_RGB(style->selectBar & 0xffffff));

			DrawTextW(hdc, text, 1, &rc2, DT_RIGHT | DT_NOPREFIX | DT_SINGLELINE);
			rc2.left = rc2.right;
			rc2.right = getRight() - getRightPadding();
			DrawTextW(hdc, text + 1, ctlMin(rightmost, end - begin - 1),
				&rc2, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE);

			SetBkMode(hdc, TRANSPARENT);
		}
		SelectObject(info->hDC, oldFont);
	}
}